﻿/* ---------------------------------------------------------- 
文件名称：WDKUsbEnum.cs 
 
作者：秦建辉 
 
MSN：splashcn@msn.com 
QQ：36748897 
 
博客：http://blog.csdn.net/jhqin 
 
开发环境： 
    Visual Studio V2010 
    .NET Framework 4 Client Profile 
 
版本历史：     
    V1.0    2011年10月10日 
            基于WDK枚举USB设备 
------------------------------------------------------------ */
using System;
using System.Collections.Generic;
using System.Management;
using System.Runtime.InteropServices;
using System.Text;

namespace com.hebg3.usb.enumerate {
  /// <summary>   
  /// USB主控制器信息   
  /// </summary>   
  public struct HostControllerInfo {
    public String PNPDeviceID;      // 设备ID   
    public String Name;             // 设备名称   
  }

  /// <summary>   
  /// USB Hub信息   
  /// </summary>   
  public struct UsbHubInfo {
    public String PNPDeviceID;      // 设备ID   
    public String Name;             // 设备名称   
    public String Status;           // 设备状态   
  }

  /// <summary>   
  /// USB HUB节点信息   
  /// </summary>   
  public struct UsbNodeInformation {
    public String DevicePath;           // 设备路径   
    public String PNPDeviceID;          // 设备ID           
    public String Name;                 // 设备名称   

    public USB_HUB_NODE NodeType;       // 节点类型   

    // USB_HUB_INFORMATION   
    public Boolean HubIsBusPowered;     // 供电方式：true-总线供电 false-独立供电   
    public Int32 NumberOfPorts;         // 端口数   
    public Int16 HubCharacteristics;    // 特征描述   
    public Byte PowerOnToPowerGood;     // 从端口加电到端口正常工作的时间间隔(以2ms为单位)   
    public Byte HubControlCurrent;      // 设备所需最大电流   

    // USB_MI_PARENT_INFORMATION   
    public Int32 NumberOfInterfaces;    // 接口数   
  }

  /// <summary>   
  /// USB设备描述符   
  /// </summary>   
  public struct UsbDeviceDescriptor {
    public Byte bDescriptorType;    // 描述符类型 USB_DEVICE_DESCRIPTOR_TYPE   
    public String UsbVersion;       // USB规格版本号   
    public Byte bDeviceClass;       // 设备类型   
    public Byte bDeviceSubClass;    // 设备子类型   
    public Byte bDeviceProtocol;    // 设备协议   
    public Byte bMaxPacketSize0;    // 最大封包大小   
    public UInt16 idVendor;         // VID   
    public UInt16 idProduct;        // PID   
    public String DeviceVersion;    // 设备版本号   
    public String Manufacturer;     // 制造商   
    public String Product;          // 产品描述   
    public String SerialNumber;     // 序列号   
    public Byte bNumConfigurations; // 配置总数   
  }

  /// <summary>   
  /// USB管道信息   
  /// </summary>   
  public struct UsbPipeInfo {
    public UInt32 ScheduleOffset;
    public Byte bDescriptorType;
    public Byte bEndpointAddress;
    public Byte bmAttributes;
    public UInt16 wMaxPacketSize;
    public Byte bInterval;
  }

  /// <summary>   
  /// USB节点连接信息   
  /// </summary>   
  public struct UsbNodeConnectionInformation {
    public String DevicePath;           // 设备路径   
    public Int32 ConnectionIndex;       // 端口号   

    public UsbDeviceDescriptor DeviceDescriptor;

    public Byte CurrentConfigurationValue;  // 当前设备配置   
    public Byte Speed;                  // 设备速度   
    public Boolean DeviceIsHub;         // 是否是集线器           
    public Int32 DeviceAddress;         // 设备地址   
    public Int32 NumberOfOpenPipes;     // 管道数   
    public USB_CONNECTION_STATUS ConnectionStatus;  // 连接状态   

    public List<UsbPipeInfo> PipeList;  // 管道信息   
  }

  /// <summary>   
  /// USB设备枚举   
  /// </summary>   
  public partial class USB {
    #region HostController
    /// <summary>   
    /// USB主控制器   
    /// </summary>   
    public static HostControllerInfo[] AllHostControllers {
      get {
        List<HostControllerInfo> HostControllers = new List<HostControllerInfo>();

        // 获取USB控制器及其相关联的设备实体   
        ManagementObjectCollection MOC = new ManagementObjectSearcher("SELECT * FROM Win32_USBController").Get();
        if( MOC != null ) {
          foreach( ManagementObject MO in MOC ) {
            HostControllerInfo Element;
            Element.PNPDeviceID = MO["PNPDeviceID"] as String;  // 设备ID   
            Element.Name = MO["Name"] as String;    // 设备描述                           
            HostControllers.Add(Element);
          }
        }

        if( HostControllers.Count == 0 )
          return null;
        else
          return HostControllers.ToArray();
      }
    }

    /// <summary>   
    /// 获取驱动键名   
    /// </summary>   
    /// <param name="PNPDeviceID">USB主控制器设备ID</param>   
    /// <returns>获取设备驱动在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class下的路径</returns>   
    public static String GetHcdDriverKeyName(String PNPDeviceID) {
      if( String.IsNullOrEmpty(PNPDeviceID) )
        return null;

      // 打开设备   
      IntPtr hHCDev = Kernel32.CreateFile(
          "\\\\.\\" + PNPDeviceID.Replace('\\', '#') + "#{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}",
          NativeFileAccess.GENERIC_WRITE,
          NativeFileShare.FILE_SHARE_WRITE,
          IntPtr.Zero,
          NativeFileMode.OPEN_EXISTING,
          IntPtr.Zero,
          IntPtr.Zero);
      if( hHCDev == Kernel32.INVALID_HANDLE_VALUE )
        return null;

      // 获取驱动键名   
      Int32 nBytesReturned;
      USB_HCD_DRIVERKEY_NAME Buffer = new USB_HCD_DRIVERKEY_NAME();
      Boolean Status = DeviceIoControl(hHCDev,
          IOCTL_GET_HCD_DRIVERKEY_NAME,
          IntPtr.Zero,
          0,
          ref Buffer,
          Marshal.SizeOf(Buffer),
          out nBytesReturned,
          IntPtr.Zero);

      // 关闭设备   
      Kernel32.CloseHandle(hHCDev);
      return Status ? Buffer.Name : null;
    }
    #endregion

    #region USBHUB
    /// <summary>   
    /// USB Hub信息集合   
    /// </summary>   
    public static UsbHubInfo[] AllUsbHubs {
      get {
        List<UsbHubInfo> UsbHubs = new List<UsbHubInfo>();

        // 获取USB控制器及其相关联的设备实体   
        ManagementObjectCollection MOC = new ManagementObjectSearcher("SELECT * FROM Win32_USBHub").Get();
        if( MOC != null ) {
          foreach( ManagementObject MO in MOC ) {
            UsbHubInfo Element;
            Element.PNPDeviceID = MO["PNPDeviceID"] as String;  // 设备ID   
            Element.Name = MO["Name"] as String;        // 设备描述       
            Element.Status = MO["Status"] as String;    // 设备状态   
            UsbHubs.Add(Element);
          }
        }

        if( UsbHubs.Count == 0 )
          return null;
        else
          return UsbHubs.ToArray();
      }
    }

    /// <summary>   
    /// USB ROOT HUB设备路径   
    /// </summary>   
    /// <param name="PNPDeviceID">USB主控制器设备ID</param>   
    /// <returns>USB ROOT HUB设备路径</returns>   
    public static String GetUsbRootHubPath(String PNPDeviceID) {
      if( String.IsNullOrEmpty(PNPDeviceID) )
        return null;

      // 打开设备   
      IntPtr hHCDev = Kernel32.CreateFile(
          "\\\\.\\" + PNPDeviceID.Replace('\\', '#') + "#{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}",
          NativeFileAccess.GENERIC_WRITE,
          NativeFileShare.FILE_SHARE_WRITE,
          IntPtr.Zero,
          NativeFileMode.OPEN_EXISTING,
          IntPtr.Zero,
          IntPtr.Zero);
      if( hHCDev == Kernel32.INVALID_HANDLE_VALUE )
        return null;

      // 获取USB ROOT HUB名称   
      Int32 nBytesReturned;
      USB_HCD_DRIVERKEY_NAME Buffer = new USB_HCD_DRIVERKEY_NAME();
      Boolean Status = DeviceIoControl(hHCDev,
          IOCTL_USB_GET_ROOT_HUB_NAME,
          IntPtr.Zero,
          0,
          ref Buffer,
          Marshal.SizeOf(Buffer),
          out nBytesReturned,
          IntPtr.Zero);

      // 关闭设备   
      Kernel32.CloseHandle(hHCDev);
      return Status ? Buffer.Name : null;
    }

    /// <summary>   
    /// USB HUB设备名称   
    /// </summary>   
    /// <param name="DevicePath">设备路径</param>   
    /// <returns>设备名称</returns>   
    public static String GetUsbHubName(String DevicePath) {
      if( String.IsNullOrEmpty(DevicePath) )
        return null;

      // 从设备路径中提取设备ID   
      String DeviceID = DevicePath.Substring(0, DevicePath.LastIndexOf('#')).Replace('#', '_');

      // 从Win32_USBHub获取设备描述   
      ManagementObjectCollection MOC = new ManagementObjectSearcher("SELECT * FROM Win32_USBHub WHERE DeviceID LIKE '" + DeviceID + "'").Get();
      if( MOC != null ) {
        foreach( ManagementObject MO in MOC ) {
          return MO["Name"] as String;
        }
      }

      return null;
    }

    /// <summary>   
    /// 获取USB HUB节点信息   
    /// </summary>   
    /// <param name="DevicePath">USB HUB设备路径</param>   
    /// <returns>节点信息</returns>   
    public static UsbNodeInformation[] GetUsbNodeInformation(String DevicePath) {
      if( String.IsNullOrEmpty(DevicePath) )
        return null;

      // 打开设备文件   
      IntPtr hHubDevice = Kernel32.CreateFile(
          "\\\\.\\" + DevicePath,
          NativeFileAccess.GENERIC_WRITE,
          NativeFileShare.FILE_SHARE_WRITE,
          IntPtr.Zero,
          NativeFileMode.OPEN_EXISTING,
          IntPtr.Zero,
          IntPtr.Zero);
      if( hHubDevice == Kernel32.INVALID_HANDLE_VALUE )
        return null;

      // 查询节点信息   
      Int32 nBytesReturned;
      USB_NODE_INFORMATION Buffer = new USB_NODE_INFORMATION();
      Boolean Status = DeviceIoControl(hHubDevice,
          IOCTL_USB_GET_NODE_INFORMATION,
          ref Buffer,
          Marshal.SizeOf(Buffer),
          ref Buffer,
          Marshal.SizeOf(Buffer),
          out nBytesReturned,
          IntPtr.Zero);

      // 关闭设备文件   
      Kernel32.CloseHandle(hHubDevice);
      if( !Status )
        return null;

      UsbNodeInformation Node = new UsbNodeInformation();
      Node.NodeType = Buffer.NodeType;    // 节点类型   
      Node.PNPDeviceID = DevicePath.Substring(0, DevicePath.LastIndexOf('#')).Replace('#', '\\'); // 设备ID   
      Node.DevicePath = DevicePath;       // 设备路径   
      Node.Name = GetUsbHubName(DevicePath);  // 设备名称   
      if( Buffer.NodeType == USB_HUB_NODE.UsbHub ) {
        Node.NumberOfPorts = Buffer.u.HubInformation.HubDescriptor.bNumberOfPorts;         // 端口数   
        Node.HubIsBusPowered = Convert.ToBoolean(Buffer.u.HubInformation.HubIsBusPowered);  // 供电方式   
        Node.HubCharacteristics = Buffer.u.HubInformation.HubDescriptor.wHubCharacteristics;
        Node.PowerOnToPowerGood = Buffer.u.HubInformation.HubDescriptor.bPowerOnToPowerGood;
        Node.HubControlCurrent = Buffer.u.HubInformation.HubDescriptor.bHubControlCurrent;
      } else {
        Node.NumberOfInterfaces = Buffer.u.MiParentInformation.NumberOfInterfaces;  // 接口数   
      }

      return new UsbNodeInformation[1] { Node };
    }
    #endregion

    #region NODECONNECTION
    /// <summary>   
    /// 获取USB节点连接信息   
    /// </summary>   
    /// <param name="DevicePath">设备路径</param>   
    /// <param name="NumberOfPorts">端口总数</param>   
    /// <returns>USB节点信息连接信息集合</returns>   
    public static UsbNodeConnectionInformation[] GetUsbNodeConnectionInformation(String DevicePath, Int32 NumberOfPorts) {
      if( String.IsNullOrEmpty(DevicePath) )
        return null;

      // 打开设备文件   
      IntPtr hHubDevice = Kernel32.CreateFile(
          "\\\\.\\" + DevicePath,
          NativeFileAccess.GENERIC_WRITE,
          NativeFileShare.FILE_SHARE_WRITE,
          IntPtr.Zero,
          NativeFileMode.OPEN_EXISTING,
          IntPtr.Zero,
          IntPtr.Zero);
      if( hHubDevice == Kernel32.INVALID_HANDLE_VALUE )
        return null;

      List<UsbNodeConnectionInformation> NodeCollection = new List<UsbNodeConnectionInformation>();

      // 枚举端口   
      USB_NODE_CONNECTION_INFORMATION_EX Buffer = new USB_NODE_CONNECTION_INFORMATION_EX();
      for( Int32 ConnectionIndex = 1; ConnectionIndex <= NumberOfPorts; ConnectionIndex++ ) {
        // 查询节点信息   
        Int32 nBytesReturned;
        Buffer.ConnectionIndex = ConnectionIndex;
        Boolean Status = DeviceIoControl(hHubDevice,
            IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
            ref Buffer,
            Marshal.SizeOf(Buffer),
            ref Buffer,
            Marshal.SizeOf(Buffer),
            out nBytesReturned,
            IntPtr.Zero);
        if( Status ) {
          // 确定语言ID   
          UInt16 LanguageID = SelectLanguageID(hHubDevice, ConnectionIndex);

          // 提取信息   
          UsbNodeConnectionInformation Node = new UsbNodeConnectionInformation();

          Node.DevicePath = DevicePath;
          Node.ConnectionIndex = Buffer.ConnectionIndex;
          Node.ConnectionStatus = Buffer.ConnectionStatus;
          if( Buffer.ConnectionStatus == USB_CONNECTION_STATUS.DeviceConnected ) {
            Node.CurrentConfigurationValue = Buffer.CurrentConfigurationValue;
            Node.Speed = Buffer.Speed;
            Node.DeviceIsHub = Convert.ToBoolean(Buffer.DeviceIsHub);
            Node.DeviceAddress = Buffer.DeviceAddress;
            Node.NumberOfOpenPipes = Buffer.NumberOfOpenPipes;

            // 设备描述符   
            Node.DeviceDescriptor.bDescriptorType = Buffer.DeviceDescriptor.bDescriptorType;
            Node.DeviceDescriptor.bDeviceClass = Buffer.DeviceDescriptor.bDeviceClass;
            Node.DeviceDescriptor.bDeviceSubClass = Buffer.DeviceDescriptor.bDeviceSubClass;
            Node.DeviceDescriptor.bDeviceProtocol = Buffer.DeviceDescriptor.bDeviceProtocol;

            Node.DeviceDescriptor.UsbVersion = BcdVersionToString(Buffer.DeviceDescriptor.bcdUSB); // USB版本号   
            Node.DeviceDescriptor.DeviceVersion = BcdVersionToString(Buffer.DeviceDescriptor.bcdDevice);    // 设备版本号   

            Node.DeviceDescriptor.idVendor = Buffer.DeviceDescriptor.idVendor;      // 厂商标识   
            Node.DeviceDescriptor.idProduct = Buffer.DeviceDescriptor.idProduct;    // 产品标识   

            if( LanguageID != 0 ) {
              if( Buffer.DeviceDescriptor.iSerialNumber != 0 ) {   // 序列号   
                Node.DeviceDescriptor.SerialNumber = GetStringDescriptor(hHubDevice,
                    Buffer.ConnectionIndex,
                    Buffer.DeviceDescriptor.iSerialNumber,
                    LanguageID);
              }

              if( Buffer.DeviceDescriptor.iManufacturer != 0 ) {   // 制造商名称   
                Node.DeviceDescriptor.Manufacturer = GetStringDescriptor(hHubDevice,
                    Buffer.ConnectionIndex,
                    Buffer.DeviceDescriptor.iManufacturer,
                    LanguageID);
              }

              if( Buffer.DeviceDescriptor.iProduct != 0 ) {   // 产品名称   
                Node.DeviceDescriptor.Product = GetStringDescriptor(hHubDevice,
                    Buffer.ConnectionIndex,
                    Buffer.DeviceDescriptor.iProduct,
                    LanguageID);
              }
            }

            Node.DeviceDescriptor.bMaxPacketSize0 = Buffer.DeviceDescriptor.bMaxPacketSize0;
            Node.DeviceDescriptor.bNumConfigurations = Buffer.DeviceDescriptor.bNumConfigurations;

            // 管道信息   
            Node.PipeList = new List<UsbPipeInfo>();
            for( Int32 PipeIndex = 0; PipeIndex < Buffer.NumberOfOpenPipes; PipeIndex++ ) {
              UsbPipeInfo PipeInfo;

              PipeInfo.ScheduleOffset = Buffer.PipeList[PipeIndex].ScheduleOffset;
              PipeInfo.bDescriptorType = Buffer.PipeList[PipeIndex].EndpointDescriptor.bDescriptorType;
              PipeInfo.bEndpointAddress = Buffer.PipeList[PipeIndex].EndpointDescriptor.bEndpointAddress;
              PipeInfo.bmAttributes = Buffer.PipeList[PipeIndex].EndpointDescriptor.bmAttributes;
              PipeInfo.wMaxPacketSize = Buffer.PipeList[PipeIndex].EndpointDescriptor.wMaxPacketSize;
              PipeInfo.bInterval = Buffer.PipeList[PipeIndex].EndpointDescriptor.bInterval;

              Node.PipeList.Add(PipeInfo);
            }
          }

          NodeCollection.Add(Node);
        }
      }

      // 关闭设备文件   
      Kernel32.CloseHandle(hHubDevice);

      // 返回结果   
      if( NodeCollection.Count == 0 )
        return null;
      else
        return NodeCollection.ToArray();
    }

    /// <summary>   
    /// 获取字符串描述符   
    /// </summary>   
    /// <param name="hHubDevice">USB Hub设备句柄</param>   
    /// <param name="ConnectionIndex">连接索引号</param>   
    /// <param name="DescriptorIndex">描述符索引号</param>   
    /// <param name="LanguageID">语言ID</param>   
    /// <returns>字符串描述符</returns>   
    public static String GetStringDescriptor(IntPtr hHubDevice, Int32 ConnectionIndex, Byte DescriptorIndex, UInt16 LanguageID) {
      USB_DESCRIPTOR_REQUEST Buffer = new USB_DESCRIPTOR_REQUEST();
      Buffer.ConnectionIndex = ConnectionIndex;
      Buffer.SetupPacket.wValue = (UInt16) ((USB_STRING_DESCRIPTOR_TYPE << 8) | DescriptorIndex);
      Buffer.SetupPacket.wIndex = LanguageID;
      Buffer.SetupPacket.wLength = MAXIMUM_USB_STRING_LENGTH;
      Int32 nBytesReturned;
      Boolean Status = DeviceIoControl(hHubDevice,
              IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
              ref Buffer,
              Marshal.SizeOf(Buffer),
              ref Buffer,
              Marshal.SizeOf(Buffer),
              out nBytesReturned,
              IntPtr.Zero);
      if( Status )
        return Buffer.Data.bString;
      else
        return null;
    }

    /// <summary>   
    /// 选择语言ID   
    /// </summary>   
    /// <param name="hHubDevice">USB Hub设备句柄</param>   
    /// <param name="ConnectionIndex">连接索引号</param>   
    /// <returns></returns>   
    public static UInt16 SelectLanguageID(IntPtr hHubDevice, Int32 ConnectionIndex) {
      // 获取支持的语言列表   
      String SupportedLanguagesString = GetStringDescriptor(hHubDevice, ConnectionIndex, 0, 0);
      if( String.IsNullOrEmpty(SupportedLanguagesString) )
        return 0;

      UInt16 UserDefaultUILanguage = com.hebg3.usb.enumerate.Environment.UserDefaultUILanguage;
      if( SupportedLanguagesString.IndexOf(Convert.ToChar(UserDefaultUILanguage)) != -1 ) {   // 用户缺省界面语言   
        return UserDefaultUILanguage;
      } else if( SupportedLanguagesString.IndexOf(Convert.ToChar(0x0409)) != -1 ) {   // 美国英语 0x0409   
        return 0x0409;
      } else {   // 第一个可选择的LANGID   
        return Convert.ToUInt16(SupportedLanguagesString[0]);
      }
    }
    #endregion

    #region EXTERNALHUB
    /// <summary>   
    /// 获取外接Hub设备路径   
    /// </summary>   
    /// <param name="ParentDevicePath">上层Hub设备路径</param>   
    /// <param name="ConnectionIndex">连接索引号</param>   
    /// <returns>外接Hub设备路径</returns>   
    public static String GetExternalHubPath(String ParentDevicePath, Int32 ConnectionIndex) {
      if( String.IsNullOrEmpty(ParentDevicePath) )
        return null;

      // 打开设备文件   
      IntPtr hParentHubDevice = Kernel32.CreateFile(
          "\\\\.\\" + ParentDevicePath,
          NativeFileAccess.GENERIC_WRITE,
          NativeFileShare.FILE_SHARE_WRITE,
          IntPtr.Zero,
          NativeFileMode.OPEN_EXISTING,
          IntPtr.Zero,
          IntPtr.Zero);
      if( hParentHubDevice == Kernel32.INVALID_HANDLE_VALUE )
        return null;

      USB_NODE_CONNECTION_DRIVERKEY_NAME Buffer = new USB_NODE_CONNECTION_DRIVERKEY_NAME();
      Buffer.ConnectionIndex = ConnectionIndex;
      Int32 nBytesReturned;
      Boolean Status = DeviceIoControl(hParentHubDevice,
              IOCTL_USB_GET_NODE_CONNECTION_NAME,
              ref Buffer,
              Marshal.SizeOf(Buffer),
              ref Buffer,
              Marshal.SizeOf(Buffer),
              out nBytesReturned,
              IntPtr.Zero);

      // 关闭设备文件   
      Kernel32.CloseHandle(hParentHubDevice);

      if( Status )
        return Buffer.DriverKeyName;
      else
        return null;
    }

    /// <summary>   
    /// 获取外接Hub设备路径   
    /// </summary>   
    /// <param name="hParentHubDevice">上层Hub设备句柄</param>   
    /// <param name="ConnectionIndex">连接索引号</param>   
    /// <returns>外接Hub设备路径</returns>   
    public static String GetExternalHubPath(IntPtr hParentHubDevice, Int32 ConnectionIndex) {
      if( hParentHubDevice == IntPtr.Zero || ConnectionIndex <= 0 )
        return null;

      USB_NODE_CONNECTION_DRIVERKEY_NAME Buffer = new USB_NODE_CONNECTION_DRIVERKEY_NAME();
      Buffer.ConnectionIndex = ConnectionIndex;
      Int32 nBytesReturned;
      Boolean Status = DeviceIoControl(hParentHubDevice,
              IOCTL_USB_GET_NODE_CONNECTION_NAME,
              ref Buffer,
              Marshal.SizeOf(Buffer),
              ref Buffer,
              Marshal.SizeOf(Buffer),
              out nBytesReturned,
              IntPtr.Zero);

      if( Status )
        return Buffer.DriverKeyName;
      else
        return null;
    }
    #endregion

    #region BCDVERSION
    /// <summary>   
    /// 版本BCD编码转字符串   
    /// </summary>   
    /// <param name="bcd">版本BCD编码</param>   
    /// <returns>版本字符串</returns>   
    private static String BcdVersionToString(UInt16 bcd) {
      StringBuilder sb = new StringBuilder(5);

      // 主版本号   
      Int32 BIT4 = (bcd >> 12) & 0x0F;
      if( BIT4 != 0 )
        sb.Append(BIT4.ToString());

      BIT4 = (bcd >> 8) & 0x0F;
      sb.Append(BIT4.ToString());

      sb.Append(".");

      // 子版本号   
      BIT4 = (bcd >> 4) & 0x0F;
      sb.Append(BIT4.ToString());

      BIT4 = bcd & 0x0F;
      if( BIT4 != 0 )
        sb.Append(BIT4.ToString());

      return sb.ToString();
    }
    #endregion
  }
}